package com.pf.utils;

import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author PanFei
 * @version 1.0.0
 * @createTime 2021/9/5
 */
@Slf4j
public class RedisUtils {

    private static final RedisTemplate<String, Object> redisTemplate = SpringContextUtils.getBean("redisTemplate", RedisTemplate.class);

    /**
     * 为redis key设置过期时间
     *
     * @param key        缓存key
     * @param expireTime 过期时间（秒）
     * @return true:设置过期时间成功；false:设置过期时间失败
     */
    public static boolean expire(String key, long expireTime) {
        try {
            if (expireTime > 0) {
                redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            log.error("RedisUtils - expire 为【{}】设置过期时间失败！", key, e);
            return false;
        }
    }

    /**
     * 获取redis key剩余过期时间
     *
     * @param key 缓存key
     * @return 剩余过期时间
     */
    public static long getExpireTime(String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * 判断是否存在某个缓存key
     *
     * @param key 缓存key
     * @return true:存在；false:不存在
     */
    public static boolean existsKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            log.error("RedisUtils - existsKey 判断键【{}】是否存在失败！", key, e);
            return false;
        }
    }

    /**
     * 为缓存key赋值
     *
     * @param key   缓存key
     * @param value 缓存值
     * @return true:赋值成功 ; false:赋值失败
     */
    public static boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            log.error("RedisUtils - set 键【{}】赋值失败！", key, e);
            return false;
        }
    }

    /**
     * 为缓存设置值和过期时间
     *
     * @param key        缓存key
     * @param value      缓存值
     * @param expireTime 过期时间（秒） expireTime要大于0。如果time小于等于0，将设置无限期
     * @return
     */
    public static boolean set(String key, Object value, long expireTime) {
        try {
            if (expireTime > 0) {
                redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            log.error("RedisUtils - set 键【{}】赋值失败！", key, e);
            return false;
        }
    }

    /**
     * 缓存值递增
     *
     * @param key   缓存key
     * @param delta 递增值
     * @return 递增后的值
     */
    public static long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 缓存值递增1
     *
     * @param key 缓存key
     * @return 递增后的值
     */
    public static long incr(String key) {
        return redisTemplate.opsForValue().increment(key);
    }

    /**
     * 缓存值递减
     *
     * @param key   缓存key
     * @param delta 递减值
     * @return 递减后的值
     */
    public static long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().decrement(key, delta);
    }

    /**
     * 缓存值递减1
     *
     * @param key 缓存值
     * @return 递减后的值
     */
    public static long decr(String key) {
        return redisTemplate.opsForValue().decrement(key);
    }

    /**
     * 删除缓存key
     *
     * @param key 缓存key
     */
    public static void del(String... key) {
        if (key != null && key.length == 1) {
            redisTemplate.delete(key[0]);
        } else {
            redisTemplate.delete(Arrays.asList(key));
        }
    }

    /**
     * 模糊删除对应的redis数据
     *
     * @param patternKey key模糊匹配
     */
    public static void fuzzyDelete(String patternKey) {
        Set<String> keys = redisTemplate.keys(patternKey);
        if (CollectionUtils.isNotEmpty(keys)) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 从redis中获取数据，转为 List<T>
     *
     * @param key
     * @param resultClazz
     * @param <T>
     * @return
     */
    public static <T> List<T> getList(String key, Class<T> resultClazz) {
        try {
            Object data = redisTemplate.opsForList().leftPop(key);
            return JacksonUtils.toList(JacksonUtils.toJsonString(data), resultClazz);
        } catch (Exception e) {
            log.error("RedisUtils - getList error！", e);
        }
        return Lists.newArrayList();
    }

    /**
     * 获取缓存key对应的信息，并以执行类型返回
     *
     * @param key         缓存key
     * @param resultClazz 返回的执行类型
     * @param <T>
     * @return 缓存值
     */
    public static <T> T get(String key, Class<T> resultClazz) {
        Object obj = redisTemplate.opsForValue().get(key);
        if (obj == null) {
            return null;
        }
        try {
            if (obj.getClass().isAssignableFrom(resultClazz)) {
                return (T) obj;
            }
        } catch (Exception e) {
            log.error("RedisUtils - get 类型换异常！", e);
        }
        return null;
    }

    /**
     * 获取缓存key的值
     *
     * @param key
     * @return
     */
    public static Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * key不存在时，为其key设置值。key存在时，返回false
     * 适用于分布式锁
     *
     * @param key        缓存key
     * @param value      缓存值
     * @param expireTime 过期时间（秒）
     * @return false：缓存key存在
     */
    public static boolean setIfAbsent(String key, Object value, long expireTime) {
        // setIfAbsent 相当于setnx(key,value)，key不存在时，为key设置值。key存在时，不设置值并且返回false;
        return redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 如果key存在，则为其设置值和过期时间
     *
     * @param key        缓存key
     * @param value      缓存值
     * @param expireTime 过期时间（秒）
     * @return
     */
    public static boolean setIfPresent(String key, Object value, long expireTime) {
        // setIfPresent 相当于redis Setex。 如果key存在，则为key设置值和过期时间
        return redisTemplate.opsForValue().setIfPresent(key, value, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 新增hash值
     *
     * @param keyName
     * @param hashKey
     * @param hashValue
     */
    public static void pushHash(String keyName, String hashKey, Object hashValue) {
        redisTemplate.opsForHash().put(keyName, hashKey, hashValue);
    }

    /**
     * 获取hash值
     *
     * @param keyName
     * @param hashKey
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getHash(String keyName, String hashKey, Class<T> clazz) {
        return (T) redisTemplate.opsForHash().get(keyName, hashKey);
    }
}
